package cn.ithers.blog.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.ithers.blog.dao.ImageDao;
import cn.ithers.blog.pojo.Image;
import cn.ithers.blog.pojo.User;
import cn.ithers.blog.response.ResponseResult;
import cn.ithers.blog.service.IImageService;
import cn.ithers.blog.service.IUserService;
import cn.ithers.blog.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

/**
 * @Author: zhanghuan
 * @date: 2020/10/2 9:40
 * @description:
 */
@Slf4j
@Service
@Transactional
public class ImageServiceImpl extends BaseService implements IImageService {

    public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd");

    @Value("${blog.image.save-path}")
    private String imagePath;

    @Value("${blog.image.max-size}")
    private long maxSize;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private IUserService userService;

    @Autowired
    private ImageDao imageDao;

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 图片上传
     * 上传的路径可以在配置文件中配置
     * 上传的内容来命名-->可以用id,->每天一个文件夹保存，限制文件的大小：通过配置文件配置
     * 保存记录到数据库中
     * ID /存储路径/url/原名称/用户id/状态/创建日期/更新日期
     *
     * @param file
     * @return
     */
    @Override
    public ResponseResult uploadImage(MultipartFile file) {
        //判断是否有文件
        if (file == null) {
            return ResponseResult.FAILED("图片不可为空");
        }
        //判断文件类型 只支持图片上传，比如，png,jpg,gif
        String contentType = file.getContentType();
        if (StrUtil.isEmpty(contentType)) {
            return ResponseResult.FAILED("文件格式错误");
        }
        //获取相关数据 文件类型，文件名称
        String originalFilename = file.getOriginalFilename();
        //denglijuan.jpg
        //png,jpg,gif
        String type = getType(contentType, originalFilename);
        if (StrUtil.isEmpty(type)) {
            return ResponseResult.FAILED("不支持此文件类型");
        }
        //限制文件的大小
        long size = file.getSize();
        log.info("size------------->" + size);
        if (size > maxSize) {
            return ResponseResult.FAILED("图片最大仅支持" + (maxSize / 1024 / 1024) + "Mb");
        }
        //创建图片的保存目录
        //规则：配置目录/日期/类型/ID.类型
        long currentTimeMillis = System.currentTimeMillis();
        String currentDate = simpleDateFormat.format(currentTimeMillis);
        String dayPath = imagePath + File.separator + currentDate;
        //E:\IdeaWorkSpace\SobBlog\images\2020_10_04
        File dayPathFile = new File(dayPath);
        //判断日期文件夹是否存在，如果不存在就创建
        if (!dayPathFile.exists()) {
            dayPathFile.mkdirs();
        }
        long targetName = idWorker.nextId();
        String targetPath = imagePath + File.separator + currentDate +
                File.separator + type + File.separator + targetName + "." + type;
        File targetFile = new File(targetPath);
        //判断类型文件夹是否存在，如果不存在就创建
        //E:\IdeaWorkSpace\SobBlog\images\2020_10_04\jpg
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        //根据我们定的规则命名
        //E:\IdeaWorkSpace\SobBlog\images\2020_10_04\jpg\762246905437618176.jpg
        //记录文件
        try {
            if (!targetFile.exists()) {
                targetFile.createNewFile();
            }
            file.transferTo(targetFile);
            //保存记录到数据库
            //Todo:
            //返回结果，包含这个图片的名称和访问路径
            //第一个是访问路径---->对应解析来
            HashMap<String, String> result = new HashMap<>();
            String resultPath = currentTimeMillis + "_" + targetName + "." + type;
            result.put("id", resultPath);
            //第二个名称是--->alt="图片描述"，如果不写前端可以用名称作为描述
            result.put("name", originalFilename);
            Image image = new Image();
            image.setContentType(type);
            image.setId(targetName + "");
            image.setCreateTime(new Date());
            image.setUpdateTime(new Date());
            image.setPath(targetFile.getPath());
            image.setName(originalFilename);
            image.setUrl(resultPath);
            image.setState("1");
            User user = userService.checkUser();
            image.setUserId(user.getId());
            //记录文件
            //保存记录到数据里
            imageDao.save(image);
            return ResponseResult.SUCCESS("图片上传成功").setData(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResponseResult.FAILED("图片上传失败");
    }

    private String getType(String contentType, String fileName) {
        String type = null;
        //如果图片的类型是png且图片名称是以png结尾
        if (Constants.ImageType.TYPE_PNG_WITH_PREFIX.equals(contentType)
                && (fileName.endsWith(Constants.ImageType.TYPE_PNG))) {
            type = Constants.ImageType.TYPE_PNG;
        } else if (Constants.ImageType.TYPE_GIF_WITH_PREFIX.equals(contentType)
                && (fileName.endsWith(Constants.ImageType.TYPE_GIF))) {
            type = Constants.ImageType.TYPE_GIF;
        } else if (Constants.ImageType.TYPE_JPEG_WITH_PREFIX.equals(contentType)
                && (fileName.endsWith(Constants.ImageType.TYPE_JPG))) {
            type = Constants.ImageType.TYPE_JPG;
        }
        return type;
    }

    /**
     * 获取图片
     *
     * @param response
     * @param imageUrl
     * @return
     */
    @Override
    public void viewImage(HttpServletResponse response, String imageUrl) throws IOException {
        //配置的目录已知
        //根据尺寸来动态返回图片给前端
        //好处：减少带宽，传输速度快。缺点：消耗后台cpu
        //推荐做法：上传上来的时候，把图片复制成三个尺寸，大，中，小
        //根据尺寸范围，返回结果即可。
        //需要日期
        String[] paths = imageUrl.split("_");
        log.info("paths-------->" + paths);
        String dayValue = paths[0];
        log.info("dayValue---------->" + dayValue);
        String format = simpleDateFormat.format(Long.parseLong(dayValue));
        log.info("format------>" + format);
        //ID
        String name = paths[1];
        log.info("name-------->" + name);
        //需要类型
        String type = name.substring(name.length() - 3);
        log.info("type---------->" + type);
        //使用日期的时间戳
        String targetPath = imagePath + File.separator + format + File.separator + type + File.separator + name;
        log.info("get image target path ====>" + targetPath);

        //从路径中读取图片
        File file = new File(targetPath);
        //写出
//        OutputStream write = null;
        BufferedOutputStream bos = null;
        //读取
//        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            response.setContentType("image/png");
            bos = new BufferedOutputStream(response.getOutputStream());
            //读取
            bis = new BufferedInputStream(new FileInputStream(file));
            byte[] buff = new byte[1024 * 8];
            int len;
            while ((len = bis.read(buff)) != -1) {
                bos.write(buff, 0, len);
            }
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                bis.close();
            }
            if (bos != null) {
                bos.close();
            }

        }
    }

    /**
     * 获取图片列表
     *
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public ResponseResult imageList(int page, int pageSize) {
        //处理page,和pageSize
        page = checkPage(page);
        pageSize = checkPageSize(pageSize);
        User user = userService.checkUser();
        if (user == null) {
            return ResponseResult.ACCOUNT_NOT_LOGIN();
        }
        //创建分页条件
        Sort sort =  Sort.by(Sort.Direction.DESC, "createTime");
        Pageable pageable = PageRequest.of(page - 1, pageSize, sort);
        final String userId = user.getId();
        Page<Image> all = imageDao.findAll(new Specification<Image>() {
            @Override
            public Predicate toPredicate(Root<Image> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                //根据用户id
                Predicate userIdPre = cb.equal(root.get("userId").as(String.class), userId);
                //根据状态
                Predicate state = cb.equal(root.get("state").as(String.class), "1");
                return cb.and(userIdPre, state);
            }
        }, pageable);
        return ResponseResult.SUCCESS("获取图片列表成功").setData(all);
    }

    /**
     * 删除图片
     *
     * @param imageId
     * @return
     */
    @Override
    public ResponseResult deleteImageById(String imageId) {
        int result = imageDao.deleteImageByUpdateState(imageId);
        return result > 0 ? ResponseResult.SUCCESS("删除图片成功") : ResponseResult.FAILED("图片不存在");
    }

    /**
     * 生成二维码图片
     *
     * @param code
     */
    @Override
    public void createQrCode(String code, HttpServletResponse response, HttpServletRequest request) {

        //检查二维码是否过期
        //todo:
        String loginState = (String) redisUtils.get(Constants.QR.KEY_PC_LOGIN_ID + code);
        if (StrUtil.isEmpty(loginState)) {
            //todo:返回一张图片，显示已经过期
            return;
        }
        String originalDomain = CustomUtils.getDomain(request);
        //生成二维码
        //二维码的内容是什么
        //1.可以简单的是一个code也就是传经来这个
        //如果是我们自己写的app来扫描，是识别并解析，亲求对应的接口，如果是第三方软件扫描，就可以识别，但是没有用，只能显示这个code
        //2.应该一个app下载地址+code，如果是自己的app扫到，切割后面的内容拿到code进行解析
        //请求对应的接口，如果是第三方的，它就会是一个网址，会访问下载app地址，去下载我们的app
        //APP_DOWNLOAD_PATH/code
        String content = originalDomain + Constants.APP_DOWNLOAD_PATH + "===" + code;
        //想要  http://localhost:8080/portal/app/===768088956859318272
        log.info("qrcontent------->" + content);

        byte[] result = QrCodeUtils.encodeQRCode(content);

        response.setContentType(QrCodeUtils.RESPONSE_CONTENT_TYPE);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(result);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //如果是第三方的应用扫描到，就会访问这个网址：http://localhost:8080/portal/app/===768088956859318272
    }
}
